/*
	Matt Wehinger
	CS 417

	This program creates a list of numbers and checks whether 
	or not each number is prime, and if the number is prime
	it is moved to another array to be written later.

	run: a.out num_threads arr_size
*/
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include <cstdlib>
using std::atoi;
#include <stdlib.h>
#include <ctime>
#include <cutil.h>
#include <cuda.h>
#include <shrUtils.h>
#include "primes_kernel.cu"

#define BLOCK_SIZE 512

void initializeArray(int *, int);
void populateArray(int *, int);
void copyArray(int *, int *, int, int);
void printArray(int*, int); 

int main(int argc, char** argv) 
{
	srand((unsigned) time(NULL));

 	int arr_size = 0;
	int *number_list, *temp_numbers_p, *temp_numbers_result;
	unsigned int timer_execution = 0, timer_memcpy = 0, timer_memcpy2 = 0;
	double memcpy_temp = 0.0, execution_time = 0.0;
	int * number_list_p;
	int iterations = 1, num_blocks, threads_per_block = 0;
	double size = 0;
	
	cudaSetDevice(1);

	arr_size = atoi(argv[1]);
	number_list = new int[arr_size];
	temp_numbers_result = new int[arr_size];

	initializeArray(number_list, arr_size);
	populateArray(number_list, arr_size);
	
	size = sizeof(int) * (double)arr_size/1000000.0;
	
	printf("array size: %d\n", arr_size);
	printf("size: %f\n", size);
	
	CUT_SAFE_CALL( cutCreateTimer(&timer_memcpy) );
	CUT_SAFE_CALL( cutStartTimer(timer_memcpy) );
	
	CUDA_SAFE_CALL( cudaMalloc((void**)&temp_numbers_p, sizeof(int)*arr_size) );
	CUDA_SAFE_CALL( cudaMemcpy(temp_numbers_p, number_list,  sizeof(int)*arr_size, cudaMemcpyHostToDevice) );
	
	CUT_SAFE_CALL( cutStopTimer(timer_memcpy) );
	
	memcpy_temp += cutGetTimerValue(timer_memcpy);
	
	dim3 dimBlock(BLOCK_SIZE);
	dim3 dimGrid(arr_size / BLOCK_SIZE + 1);
	
	printf("grid size: %d\n", dimGrid.x);
	
	CUT_SAFE_CALL( cutCreateTimer(&timer_execution) );
	CUT_SAFE_CALL( cutStartTimer(timer_execution) );
	findPrimes_kernel<<<dimGrid, dimBlock>>>(temp_numbers_p);
	cudaThreadSynchronize();
	
	//execution_time = cutStopTimer(timer_execution);
	CUT_SAFE_CALL( cutStopTimer(timer_execution) );
	printf("Execution time: %f seconds\n", cutGetTimerValue(timer_execution)/1000.0);
	
	//printf("execution time: %d\n", execution_time);
	
	CUT_SAFE_CALL( cutCreateTimer(&timer_memcpy2) );
	CUT_SAFE_CALL( cutStartTimer(timer_memcpy2) );
	
	CUDA_SAFE_CALL( cudaMemcpy(temp_numbers_result, temp_numbers_p,  sizeof(int)*arr_size, cudaMemcpyDeviceToHost) );
	
	CUT_SAFE_CALL( cutStopTimer(timer_memcpy2) );
	
	memcpy_temp += cutGetTimerValue(timer_memcpy2);
	
	//printArray(temp_numbers_result, arr_size);

	//copyArray(temp_numbers_result, number_list, threads_per_block, iterations);
	
	cudaFree( temp_numbers_p );	

	
	
	printf("time to transfer to device: %f seconds\n", memcpy_temp/1000.0);

	delete[] number_list; // frees memory

	return 0;
}

void initializeArray(int *arr, int size) 
{
	for(int i_counter = 0; i_counter < size; i_counter++)
		arr[i_counter] = 0;
}

void populateArray(int *arr, int size) 
{
	for(int i_counter = 0; i_counter < size; i_counter++)
		arr[i_counter] = rand() % 1000000 + 1;
}

void getNextSet(int *arr, int *temp_arr, int threads_per_block, int iterations)
{
	int i_counter = iterations * BLOCK_SIZE;
	int max_elements = i_counter + threads_per_block;
	
	for(int t_counter = 0; i_counter < max_elements; i_counter++, t_counter++)
	{
		temp_arr[t_counter] = arr[i_counter];
	}
}

void copyArray(int *arr, int *temp_arr, int threads_per_block, int iterations)
{
	int i_counter = iterations * BLOCK_SIZE;
	int max_elements = i_counter + threads_per_block;
	
	for(int t_counter = 0; i_counter < max_elements; i_counter++, t_counter++)
	{
		temp_arr[i_counter] = arr[t_counter];
	}
}

void printArray(int* arr, int size) 
{
	for(int i_counter = 0; i_counter < size; i_counter++)
		cout << arr[i_counter] << endl;
}
